package org.jvnc.vnc;

/**
* <br><br><center><table border="1" width="80%"><hr>
* <p>
* Copyright (C) 2006-2007 by Deon Wu
* <p>
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.0
* of the License, or (at your option) any later version.
* <p>
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU Lesser General Public License</a>
* for more details.
* <p>
* You should have received a copy of the <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">
* GNU Lesser General Public License</a> along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* <hr></table></center>
* **/

import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.IOException;

import org.jvnc.logging.VLog;
import org.jvnc.rfb.proto.RawDataGram;

public class VncInputListener implements KeyListener,MouseListener, MouseMotionListener {
	private static final VLog log = VLog.getLog(VncInputListener.class);	
	
	VncClient client = null;
	final static int CTRL_MASK  = InputEvent.CTRL_MASK;
	final static int SHIFT_MASK = InputEvent.SHIFT_MASK;
	final static int META_MASK  = InputEvent.META_MASK;
	final static int ALT_MASK   = InputEvent.ALT_MASK;	
	
	final static int KEY_DELETE = 0xffff;
	  
	byte[] eventBuf = new byte[72];
	int eventBufLen = 0;
	boolean brokenKeyPressed = false;
	int pointerMask = 0;
	private VncScreen curControl = null;
	private boolean isActive = false;
	  
	
	public VncInputListener(VncClient client){
		this.client = client;
	}

	public void keyPressed(KeyEvent evt) {
		if(isActive)
			processLocalKeyEvent(evt);
	}

	public void keyReleased(KeyEvent evt) {
		if(isActive)
			processLocalKeyEvent(evt);
	}

	public void keyTyped(KeyEvent evt) {
		if(isActive)
			evt.consume();
	}
	
	public void processLocalKeyEvent(KeyEvent evt) {
		int keyChar = evt.getKeyChar();
		if (keyChar == 0)
			keyChar = KeyEvent.CHAR_UNDEFINED;

		if (keyChar == KeyEvent.CHAR_UNDEFINED) {
			int code = evt.getKeyCode();
			if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT
					|| code == KeyEvent.VK_META || code == KeyEvent.VK_ALT)
				return;
		}
		
		boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
		int key;
		if (evt.isActionKey()) {
			switch (evt.getKeyCode()) {
			case KeyEvent.VK_HOME: 		key = 0xff50;	break;
			case KeyEvent.VK_LEFT:		key = 0xff51;	break;
			case KeyEvent.VK_UP:		key = 0xff52;	break;
			case KeyEvent.VK_RIGHT:		key = 0xff53;	break;
			case KeyEvent.VK_DOWN:		key = 0xff54;	break;
			case KeyEvent.VK_PAGE_UP:	key = 0xff55;	break;
			case KeyEvent.VK_PAGE_DOWN:	key = 0xff56;	break;
			case KeyEvent.VK_END:		key = 0xff57;	break;
			case KeyEvent.VK_INSERT:	key = 0xff63;	break;
			case KeyEvent.VK_F1:		key = 0xffbe;	break;
			case KeyEvent.VK_F2:		key = 0xffbf;	break;
			case KeyEvent.VK_F3:		key = 0xffc0;	break;
			case KeyEvent.VK_F4:		key = 0xffc1;	break;
			case KeyEvent.VK_F5:		key = 0xffc2;	break;
			case KeyEvent.VK_F6:		key = 0xffc3;	break;
			case KeyEvent.VK_F7:		key = 0xffc4;	break;
			case KeyEvent.VK_F8:		key = 0xffc5;	break;
			case KeyEvent.VK_F9:		key = 0xffc6;	break;
			case KeyEvent.VK_F10:		key = 0xffc7;	break;
			case KeyEvent.VK_F11:		key = 0xffc8;	break;
			case KeyEvent.VK_F12:		key = 0xffc9;	break;
			default:
				key = evt.getKeyCode();
				//return;
			}

		} else {
			key = keyChar;
			if (key < 0x20) {
				if (evt.isControlDown()) {
					key += 0x60;
				} else {
					switch (key) {
					case KeyEvent.VK_BACK_SPACE:	key = 0xff08;	break;
					case KeyEvent.VK_TAB:			key = 0xff09;	break;
					case KeyEvent.VK_ENTER:			key = 0xff0d;	break;
					case KeyEvent.VK_ESCAPE:		key = 0xff1b;	break;
					}
				}
			} else if (key == 0x7f) {
				key = 0xffff;						//Delete;
			} else if (key > 0xff) {
				if ((key < 0xff00 || key > 0xffff)
						&& !(key >= 0x20a0 && key <= 0x20af))
					return;
			}
		}
//		if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring
//				(key == 0xe4) || (key == 0xc4) || // XK_adiaeresis /
//													// XK_Adiaeresis
//				(key == 0xf6) || (key == 0xd6) || // XK_odiaeresis /
//													// XK_Odiaeresis
//				(key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf
//				(key == 0xa3)) { // XK_sterling
			// Make sure we do not send keypress events twice on platforms
			// with correct JVMs (those that actually report KeyPress for all
			// keys)
			if (down)
				brokenKeyPressed = true;

			if (!down && !brokenKeyPressed) {
				// We've got a release event for this key, but haven't received
				// a press. Fake it.
				eventBufLen = 0;
				writeModifierKeyEvents(evt.getModifiers());
				writeKeyEvent(key, true);
				sendBuffer(eventBufLen);
			}

			if (!down)
				brokenKeyPressed = false;
//		}

		eventBufLen = 0;
		writeModifierKeyEvents(evt.getModifiers());
		writeKeyEvent(key, down);

		if (!down)
			writeModifierKeyEvents(0);

		sendBuffer(eventBufLen);

		//client.requestFrameUpdate(x, y, width, height, isIncrement)
		evt.consume();
		client.refreshFrame();
	}	

   void writeKeyEvent(int keysym, boolean down) {
		eventBuf[eventBufLen++] = (byte) 4;
		eventBuf[eventBufLen++] = (byte) (down ? 1 : 0);
		eventBuf[eventBufLen++] = (byte) 0;
		eventBuf[eventBufLen++] = (byte) 0;
		eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff);
		eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff);
		eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff);
		eventBuf[eventBufLen++] = (byte) (keysym & 0xff);
	}

	int oldModifiers = 0;

	void writeModifierKeyEvents(int newModifiers) {
		if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
			writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);

		if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK))
			writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0);

		if ((newModifiers & META_MASK) != (oldModifiers & META_MASK))
			writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0);

		if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK))
			writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0);

		oldModifiers = newModifiers;
	}

	void sendBuffer(int length) {
		RawDataGram raw = new RawDataGram(eventBuf,0,length);
		//client.
		if(client.isConnected()){
			try {
				client.getRFBSokcet().sendDataGram(raw);
			} catch (IOException e) {
				log.error(e.toString(),e);
			}
		}
	}

	public void mouseClicked(MouseEvent arg0) {}
	public void mouseEntered(MouseEvent arg0) {}
	public void mouseExited(MouseEvent arg0) {}

	public void mousePressed(MouseEvent event) {
		if(isActive)
			writePointerEvent(event);
	}

	public void mouseReleased(MouseEvent event) {
		if(isActive)
			writePointerEvent(event);
	}

	public void mouseDragged(MouseEvent event) {
		if(isActive)
			writePointerEvent(event);		
	}

	public void mouseMoved(MouseEvent event) {
		if(isActive)
			writePointerEvent(event);
	}
	

	void writePointerEvent(MouseEvent evt){
		int modifiers = evt.getModifiers();

		int mask2 = 2;
		int mask3 = 4;

		if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
			if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
				pointerMask = mask2;
				modifiers &= ~ALT_MASK;
			} else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
				pointerMask = mask3;
				modifiers &= ~META_MASK;
			} else {
				pointerMask = 1;
			}
		} else if (evt.getID() == MouseEvent.MOUSE_RELEASED) {
			pointerMask = 0;
			if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
				modifiers &= ~ALT_MASK;
			} else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
				modifiers &= ~META_MASK;
			}
		}

		eventBufLen = 0;
		writeModifierKeyEvents(modifiers);

		int x = evt.getX();
		int y = evt.getY();

		if (x < 0)x = 0;
		if (y < 0)y = 0;

		eventBuf[eventBufLen++] = (byte) 5;
		eventBuf[eventBufLen++] = (byte) pointerMask;
		eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
		eventBuf[eventBufLen++] = (byte) (x & 0xff);
		eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
		eventBuf[eventBufLen++] = (byte) (y & 0xff);

		if (pointerMask == 0) {
			writeModifierKeyEvents(0);
		}

		sendBuffer(eventBufLen);
		client.refreshFrame();		
	}
	
	public void handOverControl(VncScreen s){
		if(curControl != null){
			curControl.addVncInputListener(null);
		}
		curControl = s;
		if(client.isConnected() && s != null){
			curControl.addVncInputListener(this);
		}
	}
	
	public void sendCtrlAltDelete(){
		eventBufLen = 0;
		writeModifierKeyEvents(CTRL_MASK | ALT_MASK);
		writeKeyEvent(KEY_DELETE, true);
		writeKeyEvent(KEY_DELETE, false);
		sendBuffer(eventBufLen);
		client.refreshFrame();
		log.debug("Send Ctrl+Alt+Delete! length" + eventBufLen);
	}
	
	public void setIsActive(boolean isActive){
		this.isActive = isActive;
	}
	
	public boolean isActive(){
		return this.isActive;
	}
}
